#pragma once

#include "../interface/Socket.hpp"
#include "../../utility/format/Format.hpp"
#include <cstring>
#include <stdexcept>

namespace network {

    class ServerConnection : public Socket {

    public:

        /**
          * @brief  默认构造一个服务端连接对象
          */
        ServerConnection() : Socket(Socket::createSocket()) {}

        explicit ServerConnection(int fd) : Socket(fd) {}

        /**
          * @brief  设置服务端套接字端口是否可重用
          * @param  on 是否开启 默认为true开启
          * @retval 是否开启成功
          */
        bool setPortReuable(bool on = true) const;

        /**
          * @brief  将该服务端套接字对象与本机的指定端口绑定
          * @note   这里会直接修改Address的IP为服务端本机IP
          * @retval 是否成功绑定
          */
        bool bind();

        /**
          * @brief  服务端开启监听绑定好的端口
          * @param  backlog 参数为等待队列最大长度 默认为128
          * @retval 是否成功监听
          */
        bool listen(int backlog = 128) const;

        /**
          * @brief  接接受客户端的连接
          * @note   accpet失败将会产生runtime_error异常
          * @retval 新建立连接的客户端Socket对象
          */
        Socket accept();

    };

#if defined(WIN32) || defined(_WIN32)

    bool ServerConnection::setPortReuable(bool on) const {
        int flag = 1;
        int ret = setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, (const char *) &flag, sizeof(int));
        return ret == 0;
    }

    Socket ServerConnection::accept() {
        Address client_address;
        socklen_t len = sizeof(*client_address.getSockAddr());
        int client_fd = ::accept(fd_, client_address.getSockAddr(), &len);
        if (client_fd == -1) {
            int error = WSAGetLastError();
            if (error == WSAEWOULDBLOCK || error == WSAEINPROGRESS || error == WSAEALREADY) {
                throw std::runtime_error("no immediately available connection requests");
            } else {
                throw std::runtime_error("accept failed with error: " + std::to_string(error));
            }
        }
        return Socket(client_fd, client_address);
    }

#else

    bool ServerConnection::setPortReuable(bool on) const {
        int flag = 1;
        int ret = setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int));
        return ret == 0;
    }

    Socket ServerConnection::accept() {
        Address client_address;
        socklen_t len = sizeof(*client_address.getSockAddr());
        int client_fd = ::accept(fd_, client_address.getSockAddr(), &len);
        if (client_fd == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
                throw std::runtime_error("no immediately available connection requests");
            } else {
                throw std::runtime_error(format::Format("accept failed with error: {0}", strerror(errno)));
            }
        }
        return Socket(client_fd, client_address);
    }

#endif

    bool ServerConnection::bind() {
        sockaddr_in *addr = getSockAddrIn();
        addr->sin_addr.s_addr = htonl(INADDR_ANY);
        bool ret = ::bind(fd_, getSockAddr(), sizeof(*getSockAddr()));
        return !ret;
    }

    bool ServerConnection::listen(int backlog) const {
        bool ret = ::listen(fd_, backlog);
        return !ret;
    }

}